﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace GreyGate.Kinect
{
    public enum Gesture{
        GAMESTOP, NOGES, FORWARD, BACK, ROTATELEFT, ROTATERIGHT, TURNAROUND
    }
    
    class KinectMoveDetection
    {
        public KinectConnect kinectCon;
        Skeleton player1, 
                 player2;

        public Texture2D kinectRGBVideo;

        public Gesture player1Gesture = Gesture.GAMESTOP,
                       player2Gesture = Gesture.GAMESTOP;

        private double player1Start_Z, 
                       player2Start_Z;

        private double player1ArmLength,
                       player2ArmLength; 

        public bool DataStored = false;

        public KinectMoveDetection(KinectConnect kinectCon)
        {
            this.kinectCon = kinectCon;
        }

        public void getMove(GameTime gameTime){
            // zeitliche Abstimmung benötigt um bestimmte Gesten über einen
            // kleinen Zeitraum zu bestimmen.
        }

        public void updateMenu()
        {
            kinectRGBVideo = kinectCon.kinectRGBVideo;
            if(runProgram2())
            {
                Console.WriteLine("playerPosition_Z: " + player1Start_Z);
                Console.WriteLine("ArmLength: "+player1ArmLength);
            }
        }

        public void update(GameTime gameTime)
        {
            // runProgram2() ist die Testversion für die Zeit, wo nur ein Spieler testet.
            // die Originalversion ist einfach ohne die 2.
           if ( runProgram2() )
           {
               // ---------------------------------- Spieler 1 -------------------------------
               updatePlayer1();
               // ----------------------------- Spieler 1 Ende ----------------------------------

                //bleibt bis zu einem 2 Spielertest mit der Funktion runProgram() auskommentiert
               //----------------------------------- Spieler 2--------------------------------
               //updatePlayer2();
               // ------------------------------- Spieler 2 Ende ----------------------------------
               
           }            
                
        }

        private void updatePlayer1()
        {
            player1Gesture = Gesture.NOGES;
            // gib das erkannte Skelett des Spielers 1 an
            player1 = kinectCon.skeletons.ElementAt(0);
            // gibt die aktuelle Position des Spielers 1 an
            double player1ActualPosition = player1.Joints[JointType.HipCenter].Position.Z;

            // mache ein update der Aktion von Spieler 1
              // Steht der Spieler in der Mitte, oder hat er sich vorwärts oder
              // rückwärts bewegt?
              player1MovedForward(player1ActualPosition);
              player1ToMiddle(player1ActualPosition);
              player1MovedBack(player1ActualPosition);
            // Hat der Spieler eine seiner Hände ausgestreckt und will rotieren?
            // Diese Befehle haben aktuell Priorität und werden vor den Bewegungen ausgeführt.
            // Solle der Spieler beide Hände ausstrecken, bekommt die rechts-Rotation den Vorrang.
            player1RotatesLeft();
            player1RotatesRight();
            player1TurnAround();
        }

        private void updatePlayer2()
        {
            player2Gesture = Gesture.NOGES;
            // gib das erkannte Skelett des Spielers 2 an
            player2 = kinectCon.skeletons.ElementAt(1);
            // gibt die aktuelle Position des Spielers 2 an
            double player2ActualPosition = player2.Joints[JointType.HipCenter].Position.Z;

            // mache ein update der Aktion von Spieler 2
              // Steht der Spieler in der Mitte, oder hat er sich vorwärts oder
              // rückwärts bewegt?
              player2MovedForward(player2ActualPosition);
              player2ToMiddle(player2ActualPosition);
              player2MovedBack(player2ActualPosition);
            // Hat der Spieler eine seiner Hände ausgestreckt und will rotieren?
            // Diese Befehle haben aktuell Priorität und werden vor den Bewegungen ausgeführt.
            // Solle der Spieler beide Hände ausstrecken, bekommt die rechts-Rotation den Vorrang.
            player2RotatesLeft();
            player2RotatesLeft();
            player2TurnAround();
        }

        // entscheidet ob das Programm laufen darf und setzt ggf. zu Anfang, die Daten zu Anfang des Spiels
        private bool runProgram()
        {
            // Wenn weniger als 2 Spieler vond er Kinect erfasst werden, werden die Steuerungselemente der beiden
            // Spieler wieder gelöscht und die Speicherung muss wiederholt werden.
            if (kinectCon.skeletons == null || kinectCon.skeletons.Count() < 2)
            {
                player1Gesture = Gesture.GAMESTOP;
                player1 = null;
                player2Gesture = Gesture.GAMESTOP;
                player2 = null;
                DataStored = false;
                return false;
            }

            // Wenn zwei Spieler von der Kinect erfasst werden, werden sie Skelette den Spielern zugewiesen.
            if (kinectCon.skeletons != null && kinectCon.skeletons.Count() == 2)
            {
                player1 = kinectCon.skeletons.ElementAt(0);
                player2 = kinectCon.skeletons.ElementAt(1);
            }

            // Wenn beiden Spielern ihre Skelette zugewiesen worden sind und ihre Daten noch nicht gespeichert wurden, sollen
            // diese Daten jetzt gespeichert werden, sobald beide Spieler zusammen die rechte Hand nach vorne ausstrecken.
            // Hierbei ist wichtig, dass beide Spieler die linke Hand normal am Körper nach unten hängen haben, damit die
            // ungefähre Armlänge erfasst werden kann.
            if (player1 != null && player2 != null && !DataStored)
            {
                // Wenn beide Spieler die rechte Hand nach vorne ausgestreckt haben, werden die Daten wie StartPosition und Armlänge
                // für die beiden Spieler gespeichert.
                // 
                if (player1.Joints[JointType.HandLeft].Position.Z < (player1.Joints[JointType.ShoulderLeft].Position.Z - 0.4f) &&
                    player2.Joints[JointType.HandLeft].Position.Z < (player2.Joints[JointType.ShoulderLeft].Position.Z - 0.4f))
                {
                    player1Start_Z = player1.Joints[JointType.HipCenter].Position.Z;
                    player1ArmLength = player1.Joints[JointType.ShoulderRight].Position.Y -
                                       player1.Joints[JointType.HandRight].Position.Y;

                    player2Start_Z = player2.Joints[JointType.HipCenter].Position.Z;
                    player2ArmLength = player2.Joints[JointType.ShoulderRight].Position.Y -
                                       player2.Joints[JointType.HandRight].Position.Y;
                    DataStored = true;
                }
                else
                {
                    return false;
                }

            }

            return true;
        }

        // nur für einen 1-Spieler Test gedacht.
        private bool runProgram2()
        {

            if (kinectCon.skeletons == null || kinectCon.skeletons.Count() < 1)
            {
                player1Gesture = Gesture.NOGES;
                player1 = null;
                DataStored = false;
                return false;
            }
            
            if (kinectCon.skeletons != null && kinectCon.skeletons.Count() == 1 )
            {
                player1 = kinectCon.skeletons.ElementAt(0);
            } 

            if(player1 != null && !DataStored)
            {
                if ( player1.Joints[JointType.HandLeft].Position.Z < (player1.Joints[JointType.ShoulderLeft].Position.Z - 0.4f) )
                {
                    player1Start_Z = player1.Joints[JointType.HipCenter].Position.Z;
                    player1ArmLength = player1.Joints[JointType.ShoulderRight].Position.Y -
                                       player1.Joints[JointType.HandRight].Position.Y;
                    DataStored = true;
                }
                else
                {
                    return false;
                }
                
            }

            return true;
        }

        // Abfragen für Spieler 2
         // Steht der Spieler vor seiner StartPosition?
        private void player1MovedForward(double actualPosition)
        {            
            if(actualPosition < player1Start_Z-0.35f){
                player1Gesture = Gesture.FORWARD;
            }
        }

        // Steht der Spieler ungfähr auf seiner StartPosition?
        private void player1ToMiddle(double actualPosition)
        {
            if (actualPosition > player1Start_Z - 0.25f &&
                 actualPosition < player1Start_Z + 0.25f)
            {
                player1Gesture = Gesture.NOGES;
            }
        }
         // Steht der Spieler hinter seiner StartPosition?
        private void player1MovedBack(double actualPosition)
        {
            if (actualPosition > player1Start_Z + 0.35f)
            {
                player1Gesture = Gesture.BACK;
            }
        }

        // Hat der Spieler den linken Arm gerade nach links ausgestreckt?
        private void player1RotatesLeft()
        {
            double player1LeftShoulderPos = player1.Joints[JointType.ShoulderLeft].Position.Y;
            double player1LeftHandPos = player1.Joints[JointType.HandLeft].Position.Y;

            if ((player1LeftShoulderPos - player1ArmLength/2) > player1LeftHandPos - 0.06f &&
                 (player1LeftShoulderPos - player1ArmLength/2) < player1LeftHandPos + 0.1f)
            {
                player1Gesture = Gesture.ROTATELEFT;
            }
        }

        // Hat der Spieler den rechten Arm gerade nach rechts ausgestreckt?
        private void player1RotatesRight()
        {
            double player1RightShoulderPos = player1.Joints[JointType.ShoulderRight].Position.Y;
            double player1RightHandPos = player1.Joints[JointType.HandRight].Position.Y;

            if ((player1RightShoulderPos + player1ArmLength/2) < player1RightHandPos + 0.06f &&
                 (player1RightShoulderPos + player1ArmLength/2) > player1RightHandPos - 0.1f)
            {
                player1Gesture = Gesture.ROTATERIGHT;
            }
        }

        // überprüft, ob der Spieler beide Hände in die Luft gestreckt hat
        private void player1TurnAround()
        {
            double player1HipPos = player1.Joints[JointType.ShoulderLeft].Position.Y;
            double player1HipLeft = player1.Joints[JointType.HipLeft].Position.X;
            double player1HipRight = player1.Joints[JointType.HipRight].Position.X;

            double player1LeftHandPosX = player1.Joints[JointType.HandLeft].Position.X;
            double player1RightHandPosX = player1.Joints[JointType.HandRight].Position.X;
            double player1LeftHandPosY = player1.Joints[JointType.HandLeft].Position.Y;
            double player1RightHandPosY = player1.Joints[JointType.HandRight].Position.Y;

            if (player1LeftHandPosY < player1HipPos && player1RightHandPosY < player1HipPos &&
                player1LeftHandPosX > player1HipLeft && player1RightHandPosX < player1HipRight )
            {
                player1Gesture = Gesture.TURNAROUND;
            }

        }



        // Abfragen für Spieler 2
        private void player2MovedForward(double actualPosition)
        {
            if (actualPosition < player2Start_Z - 0.35f)
            {
                player2Gesture = Gesture.FORWARD;
            }
        }

        private void player2ToMiddle(double actualPosition)
        {
            if (actualPosition > player2Start_Z - 0.25f &&
                 actualPosition < player2Start_Z + 0.25f)
            {
                player2Gesture = Gesture.NOGES;
            }
        }

        private void player2MovedBack(double actualPosition)
        {
            if (actualPosition > player2Start_Z + 0.35f)
            {
                player2Gesture = Gesture.BACK;
            }
        }

        private void player2RotatesLeft()
        {
            double player2LeftShoulderPos = player2.Joints[JointType.ShoulderLeft].Position.X;
            double player2LeftHandPos = player2.Joints[JointType.HandLeft].Position.X;

            if ((player2LeftShoulderPos - player2ArmLength) > player2LeftHandPos - 0.06f &&
                 (player2LeftShoulderPos - player2ArmLength) < player2LeftHandPos + 0.1f)
            {
                player2Gesture = Gesture.ROTATELEFT;
            }
        }

        private void player2RotatesRight()
        {
            double player2RightShoulderPos = player2.Joints[JointType.ShoulderRight].Position.X;
            double player2RightHandPos = player2.Joints[JointType.HandRight].Position.X;

            if ((player2RightShoulderPos + player2ArmLength) < player2RightHandPos + 0.06f &&
                 (player2RightShoulderPos + player2ArmLength) > player2RightHandPos - 0.1f)
            {
                player2Gesture = Gesture.ROTATERIGHT;
            }
        }

        private void player2TurnAround()
        {
            double player2LeftShoulderPos = player2.Joints[JointType.ShoulderLeft].Position.Y;
            double player2LeftHandPos = player2.Joints[JointType.HandLeft].Position.Y;

            double player2RightShoulderPos = player2.Joints[JointType.ShoulderRight].Position.Y;
            double player2RightHandPos = player2.Joints[JointType.HandRight].Position.Y;

            if (((player2LeftShoulderPos + player2ArmLength) > player2LeftHandPos - 0.06f &&
                  (player2LeftShoulderPos + player2ArmLength) < player2LeftHandPos + 0.3f)
                  &&
                 ((player2RightShoulderPos + player2ArmLength) > player2RightHandPos - 0.06f &&
                  (player2RightShoulderPos + player2ArmLength) < player2RightHandPos + 0.3f)
                )
            {
                player2Gesture = Gesture.TURNAROUND;
            }
        }

    }
}
